<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  
  
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <title>
    
    M_W_Q</title>
  
    <link rel="shortcut icon" href="/bolg/favicon.ico">
  
  <link rel="stylesheet" href="/bolg/css/style.css">
  
    <link rel="stylesheet" href="/bolg/fancybox/jquery.fancybox.min.css">
  
  <script src="/bolg/js/pace.min.js"></script>
</head>

<body>
<main class="content">
  <section class="jumbotron">
  <div class="video">
    
      <div class="video-frame">
        <img src="/bolg/images/ocean/overlay-hero.png" alt="Decorative image frame">
      </div>
    
    <div class="video-media">
      <video playsinline="" autoplay="" loop="" muted="" data-autoplay=""
             poster="/bolg/images/ocean/ocean.png" x5-video-player-type="h5">
        <source src="/bolg/images/ocean/ocean.mp4" type="video/mp4">
        <source src="/bolg/images/ocean/ocean.ogv" type="video/ogg">
        <source src="/bolg/images/ocean/ocean.webm" type="video/webm">
        <p>Your user agent does not support the HTML5 Video element.</p>
      </video>
      <div class="video-overlay"></div>
    </div>
    <div class="video-inner text-center text-white">
      <h1><a href="/bolg/">M_W_Q</a></h1>
      <p></p>
      <div><img src="/bolg/images/hexo-inverted.svg" class="brand" alt="M_W_Q"></div>
    </div>
    <div class="video-learn-more">
      <a class="anchor" href="#landingpage"><i class="fe fe-mouse"></i></a>
    </div>
  </div>
</section>

<div id="landingpage">
  <section class="outer">
  <article class="articles">
    
    <h1 class="page-type-title"></h1>

    
      
        

<article id="post-java8函数式编程" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/23/java8%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/">java8函数式编程</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/23/java8%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="article-date">
  <time datetime="2019-10-23T13:04:49.273Z" itemprop="datePublished">2019-10-23</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h3 id="Java-8-函数式编程"><a href="#Java-8-函数式编程" class="headerlink" title="Java 8 函数式编程"></a>Java 8 函数式编程</h3><p>java8函数式核心概念</p>
<ul>
<li>函数接口（Funtion）</li>
<li>流（stream）</li>
<li>聚合器（Collector）</li>
</ul>
<h4 id="函数接口"><a href="#函数接口" class="headerlink" title="函数接口"></a>函数接口</h4><ul>
<li>函数接口是行为的抽象</li>
<li>函数接口是数据转换器<h5 id="四大基础函数接口"><a href="#四大基础函数接口" class="headerlink" title="四大基础函数接口"></a>四大基础函数接口</h5></li>
<li>Supplier ：数据提供器，没有输入参数， 输出为类型T</li>
<li>Function：数据转换器，接收一个 T 类型的对象，返回一个 R类型的对象，提供apply，compose，andThen，identity</li>
<li>Consumer：数据消费器，  接收一个 T类型的对象，无返回值，通常用于根据T对象做些处理，提供accept，andThen</li>
<li>Predicate：条件测试器，条件测试器，接收一个 T 类型的对象，返回布尔值，通常用于传递条件函数，提供了 test (条件测试) , and-or- negate(与或非) 方法<h5 id="扩展函数接口"><a href="#扩展函数接口" class="headerlink" title="扩展函数接口"></a>扩展函数接口</h5></li>
<li>参数扩展：接受双参数Bi前缀</li>
<li>类型扩展：接受原子类型参数</li>
<li>特殊变形：BinaryOperator</li>
</ul>
<h5 id="函数接口接入参数"><a href="#函数接口接入参数" class="headerlink" title="函数接口接入参数"></a>函数接口接入参数</h5><ul>
<li>类/对象的静态方法引用，实例方法引用(PS: 使用::)</li>
<li>类的构造器（PS: Class::new）</li>
<li>lambda表达式</li>
</ul>
<h4 id="聚合器"><a href="#聚合器" class="headerlink" title="聚合器"></a>聚合器</h4><p>流的最后都是使用Collect来进行数据聚合，返回数据</p>
<h5 id="Reduce"><a href="#Reduce" class="headerlink" title="Reduce"></a>Reduce</h5><p>Reduce推导需要的是三个重要的参数</p>
<ul>
<li>init初始数据</li>
<li>二元操作符BinaryOperator</li>
<li>聚合结果源s<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> num1 = list.stream().reduce(<span class="number">0</span>, (a, b) -&gt; a + b);</span><br></pre></td></tr></table></figure>
<h5 id="聚合器四要素"><a href="#聚合器四要素" class="headerlink" title="聚合器四要素"></a>聚合器四要素</h5></li>
<li>容器的初始化（Supplier）</li>
<li>一个用于将每次二元操作的中间结果与结果容器的值进行操作并重新设置结果容器的累积器 accumulator</li>
<li>一个用于对Stream元素和中间结果进行操作的二元操作符 combiner</li>
<li>一个用于对结果容器进行最终聚合的转换器 finisher(非必须)<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line">     * Simple implementation class for &#123;@code Collector&#125;.</span><br><span class="line">     *</span><br><span class="line">     * @param &lt;T&gt; the type of elements to be collected</span><br><span class="line">     * @param &lt;R&gt; the type of the result</span><br><span class="line">     */</span><br><span class="line">static class CollectorImpl&lt;T, A, R&gt; implements Collector&lt;T, A, R&gt; &#123;</span><br><span class="line">        private final Supplier&lt;A&gt; supplier;</span><br><span class="line">        private final BiConsumer&lt;A, T&gt; accumulator;</span><br><span class="line">        private final BinaryOperator&lt;A&gt; combiner;</span><br><span class="line">        private final Function&lt;A, R&gt; finisher;</span><br><span class="line">        private final Set&lt;Characteristics&gt; characteristics;</span><br><span class="line">        CollectorImpl(Supplier&lt;A&gt; supplier,</span><br><span class="line">                      BiConsumer&lt;A, T&gt; accumulator,</span><br><span class="line">                      BinaryOperator&lt;A&gt; combiner,</span><br><span class="line">                      Function&lt;A,R&gt; finisher,</span><br><span class="line">                      Set&lt;Characteristics&gt; characteristics) &#123;</span><br><span class="line">            this.supplier = supplier;</span><br><span class="line">            this.accumulator = accumulator;</span><br><span class="line">            this.combiner = combiner;</span><br><span class="line">            this.finisher = finisher;</span><br><span class="line">            this.characteristics = characteristics;</span><br><span class="line">        &#125;</span><br><span class="line">        CollectorImpl(Supplier&lt;A&gt; supplier,</span><br><span class="line">                      BiConsumer&lt;A, T&gt; accumulator,</span><br><span class="line">                      BinaryOperator&lt;A&gt; combiner,</span><br><span class="line">                      Set&lt;Characteristics&gt; characteristics) &#123;</span><br><span class="line">            this(supplier, accumulator, combiner, castingIdentity(), characteristics);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
toList()执行操作<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Returns a &#123;@code Collector&#125; that accumulates the input elements into a</span><br><span class="line"> * new &#123;@code List&#125;. There are no guarantees on the type, mutability,</span><br><span class="line"> * serializability, or thread-safety of the &#123;@code List&#125; returned; if more</span><br><span class="line"> * control over the returned &#123;@code List&#125; is required, use &#123;@link #toCollection(Supplier)&#125;.</span><br><span class="line"> *</span><br><span class="line"> * @param &lt;T&gt; the type of the input elements</span><br><span class="line"> * @return a &#123;@code Collector&#125; which collects all the input elements into a</span><br><span class="line"> * &#123;@code List&#125;, in encounter order</span><br><span class="line"> */</span><br><span class="line">public static &lt;T&gt;</span><br><span class="line">Collector&lt;T, ?, List&lt;T&gt;&gt; toList() &#123;</span><br><span class="line">    return new CollectorImpl&lt;&gt;((Supplier&lt;List&lt;T&gt;&gt;) ArrayList::new, List::add,</span><br><span class="line">                               (left, right) -&gt; &#123; left.addAll(right); return left; &#125;,</span><br><span class="line">                               CH_ID);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>第一个参数是初始化数据通过ArrayList::new创建</li>
<li>第二个参数是执行的操作，list的add元素添加操作</li>
<li>第三个参数是执行最后的聚合操作</li>
</ul>
<h5 id="映射类聚合器"><a href="#映射类聚合器" class="headerlink" title="映射类聚合器"></a>映射类聚合器</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">private static &lt;K, V, M extends Map&lt;K,V&gt;&gt;</span><br><span class="line">    BinaryOperator&lt;M&gt; mapMerger(BinaryOperator&lt;V&gt; mergeFunction) &#123;</span><br><span class="line">        return (m1, m2) -&gt; &#123;</span><br><span class="line">            for (Map.Entry&lt;K,V&gt; e : m2.entrySet())</span><br><span class="line">                m1.merge(e.getKey(), e.getValue(), mergeFunction);</span><br><span class="line">            return m1;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>根据指定的值合并函数 mergeFunction, 返回一个map合并器，用来合并两个map里相同key的值。mergeFunction用来对两个map中相同key的值进行运算得到新的value值，如果value值为null，会移除相应的key，否则使用value值作为对应key的值。这个方法是私有的，主要为支撑 toMap，groupingBy 而生。</p>
<h5 id="自定义聚合器"><a href="#自定义聚合器" class="headerlink" title="自定义聚合器"></a>自定义聚合器</h5><p>需要实现Collector，构造四要素：</p>
<ul>
<li>可变的结果容器Supplier:初始化的参数，注意不能初始化不可变的：supplier</li>
<li>累积器 BiConsumer:使用参数来进行和终止，新元素放入结果容器中：accumulator()</li>
<li>组合器 BInaryOperator ：目前无作用：combiner()</li>
<li>最终转换器 Funcation: 最终返回数据模型：finisher() <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line">public class CollectorsImpls implements Collector&lt;Integer, List&lt;Integer&gt;, List&lt;Integer&gt;&gt; &#123;</span><br><span class="line">    /**</span><br><span class="line">     * A function that creates and returns a new mutable result container.</span><br><span class="line">     *</span><br><span class="line">     * @return a function which returns a new, mutable result container</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public Supplier&lt;List&lt;Integer&gt;&gt; supplier() &#123;</span><br><span class="line">        return () -&gt; &#123;</span><br><span class="line">            List&lt;Integer&gt; res = new ArrayList&lt;&gt;();</span><br><span class="line">            res.add(0);</span><br><span class="line">            res.add(1);</span><br><span class="line">            return res;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * A function that folds a value into a mutable result container.</span><br><span class="line">     *</span><br><span class="line">     * @return a function which folds a value into a mutable result container</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public BiConsumer&lt;List&lt;Integer&gt;, Integer&gt; accumulator() &#123;</span><br><span class="line">        return (res, num) -&gt; &#123;</span><br><span class="line">            Integer next = res.get(res.size() - 1) + res.get(res.size() - 2);</span><br><span class="line">            res.add(next);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * A function that accepts two partial results and merges them.  The</span><br><span class="line">     * combiner function may fold state from one argument into the other and</span><br><span class="line">     * return that, or may return a new result container.</span><br><span class="line">     *</span><br><span class="line">     * @return a function which combines two partial results into a combined</span><br><span class="line">     * result</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public BinaryOperator&lt;List&lt;Integer&gt;&gt; combiner() &#123;</span><br><span class="line"></span><br><span class="line">        return (res1, res2) -&gt; &#123;</span><br><span class="line">            Integer num = res1.get(res1.size()) + res2.get(res2.size());</span><br><span class="line">            res1.add(num);</span><br><span class="line">            return res1;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Perform the final transformation from the intermediate accumulation type</span><br><span class="line">     * &#123;@code A&#125; to the final result type &#123;@code R&#125;.</span><br><span class="line">     *</span><br><span class="line">     * &lt;p&gt;If the characteristic &#123;@code IDENTITY_TRANSFORM&#125; is</span><br><span class="line">     * set, this function may be presumed to be an identity transform with an</span><br><span class="line">     * unchecked cast from &#123;@code A&#125; to &#123;@code R&#125;.</span><br><span class="line">     *</span><br><span class="line">     * @return a function which transforms the intermediate result to the final</span><br><span class="line">     * result</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public Function&lt;List&lt;Integer&gt;, List&lt;Integer&gt;&gt; finisher() &#123;</span><br><span class="line">        return res -&gt; &#123;</span><br><span class="line">            res.remove(0);</span><br><span class="line">            res.remove(1);</span><br><span class="line">            return res;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Returns a &#123;@code Set&#125; of &#123;@code Collector.Characteristics&#125; indicating</span><br><span class="line">     * the characteristics of this Collector.  This set should be immutable.</span><br><span class="line">     *</span><br><span class="line">     * @return an immutable set of collector characteristics</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public Set&lt;Characteristics&gt; characteristics() &#123;</span><br><span class="line">        return Collections.emptySet();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Integer&gt; fibo = Arrays.asList(1,1,1,1).stream().collect(new CollectorsImpls());</span><br></pre></td></tr></table></figure>
<h4 id="流"><a href="#流" class="headerlink" title="流"></a>流</h4><h5 id="Stream流接口"><a href="#Stream流接口" class="headerlink" title="Stream流接口"></a>Stream流接口</h5>Stream 主要四类接口</li>
<li>流与流之间的转换：<ul>
<li>filter(过滤)</li>
<li>map(映射转换),</li>
<li>mapToInt|Long|Double, flatMap(高维结构平铺)</li>
<li>flatMapTo[Int|Long|Double] </li>
<li>sorted(排序)</li>
<li>distinct(不重复值)</li>
<li>peek(执行某种操作，流不变，可用于调试)</li>
<li>limit(限制到指定元素数量)</li>
<li>skip(跳过若干元素</li>
</ul>
</li>
<li>流与值的转化<ul>
<li>toArray（转为数组）</li>
<li>reduce（推导结果）</li>
<li>collect（聚合结果）</li>
<li>min(最小值)</li>
<li>max(最大值)</li>
<li>count (元素个数)</li>
<li>anyMatch (任一匹配)</li>
<li>allMatch(所有都匹配)</li>
<li>noneMatch(一个都不匹配)</li>
<li>findFirst（选择首元素）</li>
<li>findAny(任选一元素)</li>
</ul>
</li>
<li>遍历<ul>
<li>forEach(不保序遍历，比如并行流)</li>
<li>forEachOrdered（保序遍历)</li>
</ul>
</li>
<li>构造流<ul>
<li>empty (构造空流)</li>
<li>of (单个元素的流及多元素顺序流)</li>
<li>iterate (无限长度的有序顺序流)</li>
<li>generate (将数据提供器转换成无限非有序的顺序流)</li>
<li>concat (流的连接)</li>
<li>Builder (用于构造流的Builder对象)<h5 id="流的主要类型"><a href="#流的主要类型" class="headerlink" title="流的主要类型"></a>流的主要类型</h5></li>
</ul>
</li>
<li>Reference（对象流）</li>
<li>IntStream（int元素流）</li>
<li>LongStream（long元素流）</li>
<li>DoubleStream （double元素流）<h5 id="collector实现"><a href="#collector实现" class="headerlink" title="collector实现"></a>collector实现</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">public final &lt;R, A&gt; R collect(Collector&lt;? super P_OUT, A, R&gt; collector) &#123;</span><br><span class="line">        A container;</span><br><span class="line">        if (isParallel()</span><br><span class="line">                &amp;&amp; (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))</span><br><span class="line">                &amp;&amp; (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) &#123;</span><br><span class="line">            container = collector.supplier().get();</span><br><span class="line">            BiConsumer&lt;A, ? super P_OUT&gt; accumulator = collector.accumulator();</span><br><span class="line">            forEach(u -&gt; accumulator.accept(container, u));</span><br><span class="line">        &#125;</span><br><span class="line">        else &#123;</span><br><span class="line">            container = evaluate(ReduceOps.makeRef(collector));</span><br><span class="line">        &#125;</span><br><span class="line">        return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)</span><br><span class="line">               ? (R) container</span><br><span class="line">               : collector.finisher().apply(container);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
主要的重要的操作<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">public static &lt;T, I&gt; TerminalOp&lt;T, I&gt;</span><br><span class="line">    makeRef(Collector&lt;? super T, I, ?&gt; collector) &#123;</span><br><span class="line">        Supplier&lt;I&gt; supplier = Objects.requireNonNull(collector).supplier();</span><br><span class="line">        BiConsumer&lt;I, ? super T&gt; accumulator = collector.accumulator();</span><br><span class="line">        BinaryOperator&lt;I&gt; combiner = collector.combiner();</span><br><span class="line">        class ReducingSink extends Box&lt;I&gt;</span><br><span class="line">                implements AccumulatingSink&lt;T, I, ReducingSink&gt; &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void begin(long size) &#123;</span><br><span class="line">                state = supplier.get();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            @Override</span><br><span class="line">            public void accept(T t) &#123;</span><br><span class="line">                accumulator.accept(state, t);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            @Override</span><br><span class="line">            public void combine(ReducingSink other) &#123;</span><br><span class="line">                state = combiner.apply(state, other.state);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return new ReduceOp&lt;T, I, ReducingSink&gt;(StreamShape.REFERENCE) &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public ReducingSink makeSink() &#123;</span><br><span class="line">                return new ReducingSink();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            @Override</span><br><span class="line">            public int getOpFlags() &#123;</span><br><span class="line">                return collector.characteristics().contains(Collector.Characteristics.UNORDERED)</span><br><span class="line">                       ? StreamOpFlag.NOT_ORDERED</span><br><span class="line">                       : 0;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">final &lt;R&gt; R evaluate(TerminalOp&lt;E_OUT, R&gt; terminalOp) &#123;</span><br><span class="line">       assert getOutputShape() == terminalOp.inputShape();</span><br><span class="line">       if (linkedOrConsumed)</span><br><span class="line">           throw new IllegalStateException(MSG_STREAM_LINKED);</span><br><span class="line">       linkedOrConsumed = true;</span><br><span class="line"></span><br><span class="line">       return isParallel()</span><br><span class="line">              ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))</span><br><span class="line">              : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
Box 是一个结果值的持有者； ReducingSink 用begin, accept, combine 三个方法定义了要进行的计算；ReducingSink是有状态的流数据消费的计算抽象，阅读Sink接口文档可知。ReduceOps.makeRef(collector) 返回了一个封装了Reduce操作的ReduceOps对象。注意到，这里都是声明要执行的计算，而不涉及计算的实际过程。展示了表达与执行分离的思想。真正的计算过程启动在 ReferencePipeline.evaluate 方法里</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Evaluate the pipeline with a terminal operation to produce a result.</span><br><span class="line"> *</span><br><span class="line"> * @param &lt;R&gt; the type of result</span><br><span class="line"> * @param terminalOp the terminal operation to be applied to the pipeline.</span><br><span class="line"> * @return the result</span><br><span class="line"> */</span><br><span class="line">final &lt;R&gt; R evaluate(TerminalOp&lt;E_OUT, R&gt; terminalOp) &#123;</span><br><span class="line">    assert getOutputShape() == terminalOp.inputShape();</span><br><span class="line">    if (linkedOrConsumed)</span><br><span class="line">        throw new IllegalStateException(MSG_STREAM_LINKED);</span><br><span class="line">    linkedOrConsumed = true;</span><br><span class="line"></span><br><span class="line">    return isParallel()</span><br><span class="line">           ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))</span><br><span class="line">           : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">    final &lt;P_IN&gt; void copyInto(Sink&lt;P_IN&gt; wrappedSink, Spliterator&lt;P_IN&gt; spliterator) &#123;</span><br><span class="line">        Objects.requireNonNull(wrappedSink);</span><br><span class="line"></span><br><span class="line">        if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) &#123;</span><br><span class="line">            wrappedSink.begin(spliterator.getExactSizeIfKnown());</span><br><span class="line">            spliterator.forEachRemaining(wrappedSink);</span><br><span class="line">            wrappedSink.end();</span><br><span class="line">        &#125;</span><br><span class="line">        else &#123;</span><br><span class="line">            copyIntoWithCancel(wrappedSink, spliterator);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>Spliterator 用来对流中的元素进行分区和遍历以及施加Sink指定操作，可以用于并发计算。Spliterator的具体实现类定义在 Spliterators 的静态类和静态方法中。其中有：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">数组Spliterator:</span><br><span class="line">static final class ArraySpliterator&lt;T&gt; implements Spliterator&lt;T&gt;</span><br><span class="line">static final class IntArraySpliterator implements Spliterator.OfInt</span><br><span class="line">static final class LongArraySpliterator implements Spliterator.OfLong</span><br><span class="line">static final class DoubleArraySpliterator implements Spliterator.OfDouble</span><br><span class="line"></span><br><span class="line">迭代Spliterator:</span><br><span class="line">static class IteratorSpliterator&lt;T&gt; implements Spliterator&lt;T&gt;</span><br><span class="line">static final class IntIteratorSpliterator implements Spliterator.OfInt</span><br><span class="line">static final class LongIteratorSpliterator implements Spliterator.OfLong</span><br><span class="line">static final class DoubleIteratorSpliterator implements Spliterator.OfDouble</span><br><span class="line"></span><br><span class="line">抽象Spliterator:</span><br><span class="line">public static abstract class AbstractSpliterator&lt;T&gt; implements Spliterator&lt;T&gt;</span><br><span class="line">private static abstract class EmptySpliterator&lt;T, S extends Spliterator&lt;T&gt;, C&gt;</span><br><span class="line">public static abstract class AbstractIntSpliterator implements Spliterator.OfInt</span><br><span class="line">public static abstract class AbstractLongSpliterator implements Spliterator.OfLong</span><br><span class="line">public static abstract class AbstractDoubleSpliterator implements Spliterator.OfDouble</span><br></pre></td></tr></table></figure>
<p>每个具体的类都是实现了trySplit, forEachRemaining,tryAdvance,estimateSize,characteriscs,getComparator.</p>
<ul>
<li>trypSplit：用于流拆分，提供并发能力</li>
<li>forEachRemaining：遍历流中的数据</li>
<li>tryAdvance：消费流数据<br>整体的流程：</li>
<li>Collector定义必要的聚合操作函数</li>
<li>ReduceOps.makeRef 将 Collector 封装成一个计算对象 ReduceOps ，依赖的 ReducingSink 定义了具体的流数据消费过程</li>
<li>Spliterator 用于对流中的元素进行分区和遍历以及施加Sink指定的操作<h5 id="Pipeline"><a href="#Pipeline" class="headerlink" title="Pipeline"></a>Pipeline</h5>类 java.util.stream.AbstractPipeline 的方法 sourceSpliterator 拿到并且这里的 sourceStage 是一个 AbstractPipeline。 Pipeline 是实现流式计算的流水线抽象，也是Stream的实现类。可以看到，java.util.stream 定义了四种 pipeline: DoublePipeline, IntPipeline, LongPipeline, ReferencePipeline。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">    public final Stream&lt;P_OUT&gt; filter(Predicate&lt;? super P_OUT&gt; predicate) &#123;</span><br><span class="line">        Objects.requireNonNull(predicate);</span><br><span class="line">        return new StatelessOp&lt;P_OUT, P_OUT&gt;(this, StreamShape.REFERENCE,</span><br><span class="line">                                     StreamOpFlag.NOT_SIZED) &#123;</span><br><span class="line">            @Override</span><br><span class="line">            Sink&lt;P_OUT&gt; opWrapSink(int flags, Sink&lt;P_OUT&gt; sink) &#123;</span><br><span class="line">                return new Sink.ChainedReference&lt;P_OUT, P_OUT&gt;(sink) &#123;</span><br><span class="line">                    @Override</span><br><span class="line">                    public void begin(long size) &#123;</span><br><span class="line">                        downstream.begin(-1);</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    @Override</span><br><span class="line">                    public void accept(P_OUT u) &#123;</span><br><span class="line">                        if (predicate.test(u))</span><br><span class="line">                            downstream.accept(u);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
套路基本一样，关键点在于 accept 方法。filter 只在满足条件时将值传给下一个 pipeline, 而 map 将计算的值传给下一个 pipeline. StatelessOp 没有什么逻辑，JDK文档解释是：Base class for a stateless intermediate stage of a Stream。相应还有一个 StatefulOp, Head。 这些都是 ReferencePipeline ，负责将值在 pipeline 之间传递，交给 Sink 去计算<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">static class Head&lt;E_IN, E_OUT&gt; extends ReferencePipeline&lt;E_IN, E_OUT&gt;</span><br><span class="line">abstract static class StatelessOp&lt;E_IN, E_OUT&gt; extends ReferencePipeline&lt;E_IN, E_OUT&gt;</span><br><span class="line">abstract static class StatefulOp&lt;E_IN, E_OUT&gt; extends ReferencePipeline&lt;E_IN, E_OUT&gt;</span><br></pre></td></tr></table></figure></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/23/java8%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/" data-id="ck32z8yug000a69db7lwc6mgi"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-自定义注解" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/23/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3/">自定义注解（hibernate-validator实现）</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/23/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3/" class="article-date">
  <time datetime="2019-10-23T12:11:20.109Z" itemprop="datePublished">2019-10-23</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h3 id="自定义注解（hibernate-validator实现）"><a href="#自定义注解（hibernate-validator实现）" class="headerlink" title="自定义注解（hibernate-validator实现）"></a>自定义注解（hibernate-validator实现）</h3><hr>
<h4 id="pom添加"><a href="#pom添加" class="headerlink" title="pom添加"></a>pom添加</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span>    </span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.hibernate<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span>    </span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hibernate-validator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>4.3.1.Final<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h4 id="注解编写"><a href="#注解编写" class="headerlink" title="注解编写"></a>注解编写</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*** * 功能描述: &lt;br&gt; * &lt; 字符串不为空串&gt; * * </span></span><br><span class="line"><span class="comment"><span class="doctag">@param</span> null * <span class="doctag">@return</span>: * </span></span><br><span class="line"><span class="comment"><span class="doctag">@since</span>: 1.0.0 * </span></span><br><span class="line"><span class="comment"><span class="doctag">@Author</span>:Administrator * </span></span><br><span class="line"><span class="comment"><span class="doctag">@Date</span>: 2019/8/16 14:24 */</span></span><br><span class="line"><span class="meta">@Target</span>(&#123;METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER,TYPE_USE&#125;)</span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Constraint</span>(validatedBy =CheckStringNotBlankCaseValidator.class)</span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> NotBlank &#123;   </span><br><span class="line"><span class="function">String <span class="title">message</span><span class="params">()</span> <span class="keyword">default</span> "字符为空"</span>;    </span><br><span class="line">Class&lt;?&gt;[] groups() <span class="keyword">default</span> &#123;&#125;;    </span><br><span class="line">Class&lt;? extends Payload&gt;[] payload() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="具体校验实现"><a href="#具体校验实现" class="headerlink" title="具体校验实现"></a>具体校验实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CheckStringNotBlankCaseValidator</span> <span class="keyword">implements</span> <span class="title">ConstraintValidator</span>&lt;<span class="title">NotBlank</span>, <span class="title">String</span>&gt; </span>&#123;    </span><br><span class="line"><span class="keyword">private</span> String value;   </span><br><span class="line"><span class="meta">@Override</span>    </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initialize</span><span class="params">(NotBlank constraintAnnotation)</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Override</span>    </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isValid</span><span class="params">(String value, ConstraintValidatorContext context)</span> </span>&#123;       </span><br><span class="line"><span class="keyword">return</span> StringUtils.isNotBlank(value);   </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="校验工具类"><a href="#校验工具类" class="headerlink" title="校验工具类"></a>校验工具类</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ValidatorConfiguration</span> </span>&#123;    </span><br><span class="line"><span class="comment">/**     * 快速失败处理Validator初始化     * <span class="doctag">@return</span>     */</span>    </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Validator <span class="title">validatorFast</span><span class="params">()</span> </span>&#123;        </span><br><span class="line">ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)</span><br><span class="line">.configure()               </span><br><span class="line">.addProperty( <span class="string">"hibernate.validator.fail_fast"</span>, <span class="string">"true"</span> )               </span><br><span class="line">.buildValidatorFactory();        </span><br><span class="line">Validator validator = validatorFactory.getValidator();        </span><br><span class="line"><span class="keyword">return</span> validator;   </span><br><span class="line">&#125;   </span><br><span class="line"><span class="comment">/***     * 失败处理Validator初始化     * <span class="doctag">@return</span>     */</span>    </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Validator <span class="title">validator</span><span class="params">()</span> </span>&#123;        </span><br><span class="line">ValidatorFactory validatorFactory =Validation.byProvider(HibernateValidator.class)                </span><br><span class="line">.configure()                </span><br><span class="line">.buildValidatorFactory();       </span><br><span class="line">Validator validator = validatorFactory.getValidator();        </span><br><span class="line"><span class="keyword">return</span> validator;    </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="注解使用工具"><a href="#注解使用工具" class="headerlink" title="注解使用工具"></a>注解使用工具</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ValidatorUtil</span> </span>&#123;    </span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Validator validator = ValidatorConfiguration.validator();    </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function">ValidationResult <span class="title">validateEntity</span><span class="params">(T obj)</span> </span>&#123;        </span><br><span class="line">ValidationResult result = <span class="keyword">new</span> ValidationResult();        </span><br><span class="line">Set&lt;ConstraintViolation&lt;T&gt;&gt; set = validator.validate(obj, Default.class);       </span><br><span class="line"><span class="keyword">if</span> (CollectionUtils.isNotEmpty(set)) &#123;            </span><br><span class="line">result.setHasErrors(<span class="keyword">true</span>);            </span><br><span class="line">Map&lt;String, String&gt; errorMsg = <span class="keyword">new</span> HashMap&lt;&gt;();            </span><br><span class="line"><span class="keyword">for</span> (ConstraintViolation&lt;T&gt; cv : set) &#123;                </span><br><span class="line">errorMsg.put(cv.getPropertyPath().toString(), </span><br><span class="line">cv.getMessage());           </span><br><span class="line">&#125;           </span><br><span class="line">result.setErrorMsg(errorMsg);        </span><br><span class="line">&#125;        </span><br><span class="line"><span class="keyword">return</span> result;    </span><br><span class="line">&#125;   </span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function">ValidationResult <span class="title">validateProperty</span><span class="params">(T obj, String propertyName)</span> </span>&#123;        </span><br><span class="line">ValidationResult result = <span class="keyword">new</span> ValidationResult();        </span><br><span class="line">Set&lt;ConstraintViolation&lt;T&gt;&gt; set = validator.validateProperty(obj, propertyName, Default.class);       </span><br><span class="line"><span class="keyword">if</span> (CollectionUtils.isNotEmpty(set)) &#123;            </span><br><span class="line">result.setHasErrors(<span class="keyword">true</span>);            </span><br><span class="line">Map&lt;String, String&gt; errorMsg = <span class="keyword">new</span> HashMap&lt;&gt;();            </span><br><span class="line"><span class="keyword">for</span> (ConstraintViolation&lt;T&gt; cv : set) &#123;                </span><br><span class="line">errorMsg.put(propertyName, cv.getMessage());           </span><br><span class="line">&#125;            </span><br><span class="line">result.setErrorMsg(errorMsg);       </span><br><span class="line">&#125;       </span><br><span class="line"><span class="keyword">return</span> result;   </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/23/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3/" data-id="ck32z8ytu000769dballra3la"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-Linux下安装jdk（Centos版）" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/Linux%E4%B8%8B%E5%AE%89%E8%A3%85jdk%EF%BC%88Centos%E7%89%88%EF%BC%89/">Linux下安装jdk（Centos版）</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/Linux%E4%B8%8B%E5%AE%89%E8%A3%85jdk%EF%BC%88Centos%E7%89%88%EF%BC%89/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a><strong>准备工作</strong></h2><ol>
<li>下载jdk，选择你需要的版本。（<a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" rel="noopener">下载</a>）</li>
<li>linux环境（需要理解基本的命令）</li>
</ol>
<h2 id="基本的命令"><a href="#基本的命令" class="headerlink" title="基本的命令"></a><strong>基本的命令</strong></h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sudo                     //获得root权限</span><br><span class="line">mkdir xxx                //创建XXX文件夹</span><br><span class="line">mv file path             //移动file到path路径</span><br><span class="line">tar -zcvf   xxx.tar.gz   //解压xxx文件</span><br></pre></td></tr></table></figure>


<h2 id="开始创建安装位置"><a href="#开始创建安装位置" class="headerlink" title="开始创建安装位置"></a><strong>开始创建安装位置</strong></h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">//拥有root权限（需要输入你的用户密码）</span><br><span class="line">sudo              </span><br><span class="line">//这是我的安装目录，你可以根据自己想安装的位置进行创建</span><br><span class="line">mkdir /usr/java  </span><br><span class="line">//移动下载好的jdk到你想要安装的位置 </span><br><span class="line">mv jdk-8u112-linux-x64.tar.gz /usr/java/</span><br><span class="line">//解压tar.gz压缩包</span><br><span class="line">tar -zxvf jdk-8u112-linux-x64.tar.gz</span><br><span class="line">//设置java环境</span><br><span class="line">vim ~/.bashrc</span><br><span class="line">//在打开的文件里面编写如下文件（注意JAVA_HOME是你自己上面jdk的bin路径）</span><br><span class="line">export JAVA_HOME=/usr/java/jdk1.8.0_112</span><br><span class="line">export JRE_HOME=$&#123;JAVA_HOME&#125;/jre</span><br><span class="line">export CLASSPATH=.:$&#123;JAVA_HOME&#125;/lib:$&#123;JRE_HOME&#125;/lib</span><br><span class="line">export PATH=$&#123;JAVA_HOME&#125;/bin:$PATH</span><br><span class="line">//生效jdk配置</span><br><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure>
<h2 id="查看是否生效"><a href="#查看是否生效" class="headerlink" title="查看是否生效"></a><strong>查看是否生效</strong></h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">java -version</span><br><span class="line">//出现如下结果</span><br><span class="line">java version &quot;1.8.0_112&quot;</span><br><span class="line">Java(TM) SE Runtime Environment (build 1.8.0_112-b15)</span><br><span class="line">Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)</span><br></pre></td></tr></table></figure>

<h2 id="多说点"><a href="#多说点" class="headerlink" title="多说点"></a><strong>多说点</strong></h2><p>如果你看其他教程的，会发现好多都是使用如下命令中配置jdk环境，不要在意这些两种方式都是可以的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/profile</span><br></pre></td></tr></table></figure>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/Linux%E4%B8%8B%E5%AE%89%E8%A3%85jdk%EF%BC%88Centos%E7%89%88%EF%BC%89/" data-id="ck32z8ytn000269dbbdk80kv0"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-Mysql max_allowed_packet的问题" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/Mysql%20max_allowed_packet%E7%9A%84%E9%97%AE%E9%A2%98/">Mysql的max_allowed_packet问题</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/Mysql%20max_allowed_packet%E7%9A%84%E9%97%AE%E9%A2%98/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h2 id="Mysql的max-allowed-packet问题"><a href="#Mysql的max-allowed-packet问题" class="headerlink" title="Mysql的max_allowed_packet问题"></a><strong>Mysql的max_allowed_packet问题</strong></h2><p>  在初学mysql时我们可能不会遇到max_allowed_packet的问题，但当你需要一次写入大量的数据时，就可能会遇到mysql提示写入的数据过大问题。</p>
<h2 id="1-什么是max-allowed-packet："><a href="#1-什么是max-allowed-packet：" class="headerlink" title="1.什么是max_allowed_packet："></a>1.什么是max_allowed_packet：</h2><p>  max_allowed_packet是mysql允许插入一条数据的大小</p>
<h2 id="2-查看max-allowed-packe："><a href="#2-查看max-allowed-packe：" class="headerlink" title="2.查看max_allowed_packe："></a>2.查看max_allowed_packe：</h2><p>  在mysql的命令行模式中使用</p>
<pre><code>show VARIABLES like     &apos;%max_allowed_packet%’;</code></pre><p>![@查看结果如下 ](<a href="http://img.blog.csdn.net/20170419133351579?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTV9BTEw=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/" target="_blank" rel="noopener">http://img.blog.csdn.net/20170419133351579?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTV9BTEw=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/</a><br>gravity/SouthEast )<br>3.解决的办法：(对mysql的max_allowed_packet的修改。（3种方式）)</p>
<hr>
<ul>
<li><p>修改my.cnf文件，设置max_allowed_packet=16M，该方法简单并且永久的生效。完成这些以后必须做的就是对mysql的重启和项目中对数据库要重新连接，也就是说重启你的服务（以spring boot来说，其他类似）。</p>
</li>
<li><p>进入mysql的命令行模式，使用set global max_allowed_packet = 2 * 1024 * 1024 * 10;来进行设置大小，可以根据你项目的需求来进行调整，在出错误时mysql是会提示你插入的数据包的大小和你当前允许的数据包的大小的。这是不需要对musql进行重启的，重启后会恢复到以前的设置，所以切记不要重启。当然和上一个相同你的服务还是要重启的。</p>
</li>
<li><p>在终端 使用mysql –max_allowed_packet=32M 来进行设置。（没有试过，有兴趣的可以试试。然后记得回复我）</p>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/Mysql%20max_allowed_packet%E7%9A%84%E9%97%AE%E9%A2%98/" data-id="ck32z8yto000369db571tfs9g"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-hello-world" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/hello-world/">Hello World</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/hello-world/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/hello-world/" data-id="ck32z8ytq000469dbbpyqb1v6"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-Hexo+github搭建自己的专属博客" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/Hexo+github%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%93%E5%B1%9E%E5%8D%9A%E5%AE%A2/">Hexo+github搭建自己的专属博客</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/Hexo+github%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%93%E5%B1%9E%E5%8D%9A%E5%AE%A2/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h2 id="Hexo是什么？"><a href="#Hexo是什么？" class="headerlink" title="Hexo是什么？"></a><em>Hexo是什么？</em></h2><p>  简单的来说<a href="https://hexo.io/docs/" target="_blank" rel="noopener">hexo</a>是一个博客框架，它可以快速的帮助你搭建一个博客。它会将你写的博客快速的转换成静态文件。类似现在前后台分离使用的shepherd。</p>
<h2 id="安装环境"><a href="#安装环境" class="headerlink" title="安装环境"></a><em>安装环境</em></h2><p>它的安装需要一定的环境如下：</p>
<table>
<thead>
<tr>
<th align="right">环境</th>
<th align="right">Node.js</th>
<th align="right">git</th>
<th>npm</th>
</tr>
</thead>
<tbody><tr>
<td align="right">版本</td>
<td align="right">&lt; 7.0</td>
<td align="right">不限</td>
<td>不限</td>
</tr>
<tr>
<td align="right">验证安装</td>
<td align="right">node -v</td>
<td align="right">git –verison</td>
<td>npm -v</td>
</tr>
</tbody></table>
<p>一般情况你在安装的时候node.js和npm是在一起的不需要单独的下载。</p>
<h2 id="Node-js的安装"><a href="#Node-js的安装" class="headerlink" title="Node.js的安装"></a><em>Node.js的安装</em></h2><p>  我只讲讲在mac和linux环境中的搭建，实在抱歉没时间去windows中去验证。（windows大家可以参考这个<a href="http://www.runoob.com/nodejs/nodejs-install-setup.html" target="_blank" rel="noopener">Windows搭建node</a>）</p>
<ol>
<li><p>如下图的<a href="https://nodejs.org/en/download/" target="_blank" rel="noopener">Node.js</a>的官方网站下载<br><img src="http://upload-images.jianshu.io/upload_images/5862120-3b508a8d7437f074.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="enter image description here"></p>
</li>
<li><p>安装nvm在Mac环境下</p>
<pre><code>wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash</code></pre></li>
</ol>
<p>  来进行nvm安装提出这个东西完全是这东西是非常好的mac软件安装管理工具，例如Ubuntu的apt-get和centos的yum。</p>
<ol start="3">
<li><p>安装node.js 和 npm</p>
<pre><code>nvm install 6.9.1    #仅仅需要这个
nvm install ls       #看看当前你安装的有哪些版本
nvm use 6.9.1        #使用哪个版本</code></pre></li>
</ol>
<p>  我们仅仅需要这样一个命令就能完成对node的安装。当然还有其他的非常好用的命令，有兴趣的可以自己去探索，在这里这些不重要，你只需要知道这一个就好。一般情况node和npm是集成的所以node成功npm也就成功了。</p>
<h2 id="安装-git"><a href="#安装-git" class="headerlink" title="安装 git"></a><strong>安装 git</strong></h2><ol>
<li><p>安装 git </p>
<pre><code>sudo brew install git</code></pre></li>
</ol>
<p>  相信大家都不陌生就不都说了，git这东西估计开发人员都有在使用。就不在啰嗦了。不太清楚的可以google也可以使用文末我的邮箱来询问我。</p>
<ol start="2">
<li><p>生成SSH key</p>
<pre><code>ssh-keygen -t rsa -C  &quot;xxxx.163.com&quot;</code></pre></li>
</ol>
<p>  其实”xxx.163.com”只是个邮箱你随意，一般和github的注册邮箱一样。然后在根目录下生成.ssh的文件夹。（centos是在/目录下，mac在你的用户的根目录）</p>
<p>如图：id_rsa是你的私匙，id_rsa.pub是你的公匙一会要在github中使用<img src="http://upload-images.jianshu.io/upload_images/5862120-076cfd5cd46b6a98.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="enter image description here"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/5862120-1545624450dfbab4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="enter image description here"></p>
<p>key就是在github上添加id_rsa.pub中内容的位置，title这个不重要你写什么也可以只要自己方便分辨就好。</p>
<h2 id="github上创建一个仓库"><a href="#github上创建一个仓库" class="headerlink" title="github上创建一个仓库"></a><strong>github上创建一个仓库</strong></h2><ul>
<li><p>下图就是你新建的仓库的命名（命名一定要和Owner名字相同然后多加了一个github.io的后缀，这点很重要。）<br><img src="http://upload-images.jianshu.io/upload_images/5862120-5061b3adfad7ee40.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="enter image description here"></p>
</li>
<li><p>仓库创建成功后需要记得这个地址，当然你也可以在后面用到的时候在到github中查看<img src="http://upload-images.jianshu.io/upload_images/5862120-ee280afd00085373.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
</li>
</ul>
<h2 id="查看的环境"><a href="#查看的环境" class="headerlink" title="查看的环境"></a><strong>查看的环境</strong></h2><ul>
<li><p>npm是否成功运行</p>
<pre><code>npm -v</code></pre></li>
<li><p>node是否成功，并且版本是否低于7.0</p>
<pre><code>node -v</code></pre></li>
<li><p>github是否成功</p>
<pre><code>git --version</code></pre></li>
</ul>
<p>  如果每个都能显示出版本号，那恭喜你基本的环境已经完成，你马上就可以拥有自己的博客家园了，下面我就请出我们今天的主角Hexo。</p>
<h2 id="Hexo的安装"><a href="#Hexo的安装" class="headerlink" title="Hexo的安装"></a><strong>Hexo的安装</strong></h2><ul>
<li><p>这个仅仅需要一条命令</p>
<pre><code>npm install -g hexo-cli</code></pre></li>
<li><p>是不是感觉简单到爆，就这样你的基本博客环境就已经搭建起来了。</p>
</li>
</ul>
<h2 id="开始搭建博客"><a href="#开始搭建博客" class="headerlink" title="开始搭建博客"></a><strong>开始搭建博客</strong></h2><ul>
<li><p>   hexo init   [name]      ##初始化仓库</p>
</li>
<li><p>name是选择输入的，你可以输入你想的名字，然后初始化的东西就放在你的name下。</p>
<pre><code>cd name       ##进入name目录查看生成的目录</code></pre></li>
<li><p>下图就是里面的文件目录，当前最重要的是_config.yml文件<img src="http://upload-images.jianshu.io/upload_images/5862120-6bcd2eb73b83f954.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<pre><code>vim _config.yml    ##进入到文件的编辑中</code></pre></li>
<li><p>找到下图中位置将里面的东西添加到你的文件中，记得将repository中的地址换成上面让你记的那个地址，那是你的github仓库地址。<img src="http://upload-images.jianshu.io/upload_images/5862120-e0d7e3c262b42934.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
</li>
</ul>
<h2 id="开始使用hexo"><a href="#开始使用hexo" class="headerlink" title="开始使用hexo"></a><strong>开始使用hexo</strong></h2><ul>
<li><p>使用generate来生成静态文件</p>
<pre><code>hexo generate</code></pre></li>
<li><p>使用deploy来提交到github上，就是将其部署。</p>
<pre><code>hexo deploy</code></pre></li>
<li><p>此时你已经成功的部署到线上了，你可以使用<a href="http://mawenqiang.github.io来进行访问。" target="_blank" rel="noopener">http://mawenqiang.github.io来进行访问。</a></p>
</li>
<li><p>本地调试执行下面的命令，然后使用<a href="http://localhost:4000来进行本地的访问" target="_blank" rel="noopener">http://localhost:4000来进行本地的访问</a></p>
<pre><code>hexo server</code></pre></li>
</ul>
<h2 id="经常使用的命令"><a href="#经常使用的命令" class="headerlink" title="经常使用的命令"></a><strong>经常使用的命令</strong></h2><ul>
<li><p>下面的命令是进出需要用到的</p>
<pre><code>hexo generate       ##生成静态文件
hexo deploy         ##部署到线上
hexo sever          ##本地查看
hexo new &quot;新的文章名&quot; ##新建文章</code></pre></li>
<li><p>新文件存放在 source/_posts文件中</p>
</li>
</ul>
<h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a><strong>问题</strong></h2><ul>
<li>在部署的时候可能会遇到很多问题，如果解决不了可以将问题发送到我的邮箱，这里就不一一的列举问题了。</li>
<li>hexo中还有很多的命令可以使用，有兴趣的可以自己探索一下。</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/Hexo+github%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%93%E5%B1%9E%E5%8D%9A%E5%AE%A2/" data-id="ck32z8ytk000169db90audn5a"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-可重复执行" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/%E5%8F%AF%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C/">可重复执行</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/%E5%8F%AF%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h4 id="1-什么是可重复执行"><a href="#1-什么是可重复执行" class="headerlink" title="1.什么是可重复执行"></a>1.什么是可重复执行</h4><p>可重复执行是系统功能服务方在第三方需要调用时可以重复的调用，不会影响系统产生异常数据，导致数据资损而产生一系列的问题进行数据维护（包括防重复，幂等）</p>
<h5 id="防重复"><a href="#防重复" class="headerlink" title="防重复"></a>防重复</h5><p>防重是在防止系统重复执行的系统相同的服务逻辑，基础的理解是该服务在同一个请求下仅仅允许一次执行。</p>
<h5 id="幂等"><a href="#幂等" class="headerlink" title="幂等"></a>幂等</h5><p>幂等（idempotent、idempotence）是一个数学与计算机学概念，常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数，或幂等方法，是指可以使用相同参数重复执行，并能获得相同结果的函数。这些函数不会影响系统状态，也不用担心重复执行会对系统造成改变。例如，“select()”函数就是一个幂等函数,无论多次执行，其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现</p>
<h4 id="2-天然幂等无需考虑场景"><a href="#2-天然幂等无需考虑场景" class="headerlink" title="2. 天然幂等无需考虑场景"></a>2. 天然幂等无需考虑场景</h4><ol>
<li>查询操作，不涉及到数据的改动，多次查询都是一样的</li>
<li>删除操作，删除数据使用唯一号，而且后续不会再次使用的唯一号能保证唯一幂等</li>
</ol>
<h4 id="3-考虑时机"><a href="#3-考虑时机" class="headerlink" title="3. 考虑时机"></a>3. 考虑时机</h4><p>下面的的情况一般是应用于会影响到数据的系统服务</p>
<ol>
<li>其它系统服务调用（rpc，rest等方式）：现在微服务的架构特点，服务拆分和架构上的功能服务拆分导致我们必须是有rpc，rest或者mq的方式来进行系统间的交互。</li>
<li>批处理任务调度异常（重复调用）：调度任务系统出现异常，导致已经调用过的任务再次发起需要我们对其进行场景考虑</li>
<li>批处理任务同时唤醒多台机器执行同一个任务：批处理任务配置时同时唤醒多台机器进行数据数据处理，这就需要N台机器获取数据要执行的数据需要进行区分，防止多台机器对同一个数据进行修改，导致数据异常，这个时候就需要考虑到调度任务唤起，任务获取和执行这个三个方面来进行解决问题不同的问题具体对待</li>
<li>批处理断点续起问题：任务时间太久导致任务执行一半失败，一种方式是回滚前面已执行数据，而是对已执行的进行标记对未执行的继续执行，在大量任务中，我们会更加偏向第二种方式。</li>
</ol>
<h4 id="4-解决方案中需要了解的知识"><a href="#4-解决方案中需要了解的知识" class="headerlink" title="4. 解决方案中需要了解的知识"></a>4. 解决方案中需要了解的知识</h4><ol>
<li>悲观锁<br>悲观锁是对事情保持悲观的态度，认为会产生别人进行修改数据，有冲突操作，每次进行执行的时候就进行加锁不允许其它的进行操作.有数据库方面的表锁，行数，java里面的<a href="https://www.jianshu.com/p/7c7a5df5bda6" target="_blank" rel="noopener">闭锁</a>，<a href="https://www.jianshu.com/p/86c720945fdc" target="_blank" rel="noopener">栅栏</a>，<a href="https://www.jianshu.com/p/0524191dcf76" target="_blank" rel="noopener">信号量</a>，<a href="https://blog.csdn.net/tongdanping/article/details/79647337" target="_blank" rel="noopener">synchronized</a>，ReentrantLock都是悲观锁的典型设计<ul>
<li>数据库：我们的行锁，表锁都是悲观锁</li>
<li>redis分布式锁：目前使用的也是悲观锁的概念</li>
</ul>
</li>
<li>乐观锁<br>乐观锁假设认为数据一般情况下不会造成冲突，所以在数据进行提交更新的时候，才会正式对数据的冲突与否进行检测，如果发现冲突了，则让返回用户错误的信息，让用户决定如何去做或者使用系统策略（状态机和CAS）<ul>
<li>状态机：状态机一般是指定一个状态来进行标识，应用级别一般是单应用防止并发，数据库级别可以是用来做分布式控制</li>
<li>CAS：有三个操作数据，A(预期值)，V(内存里面当前值)，B(修改值)：只有A和V数据相同时才会将V值修改为B，否则不进行任何操作。是并发情况下保证数据的正常性的一种方案。</li>
</ul>
</li>
<li>去重表<br>去重表指的利用数据库创建一个表，利用数据库唯一索引来保证每一个条数据都是不一样的，通过策略保证数据不会重复执行和执行结果幂等</li>
<li>token唯一防重复<br>token是类似于唯一标识<a href="https://jwt.io/" target="_blank" rel="noopener">JTW</a>（java Token Web）经常用于<a href="https://oauth.net/2/" target="_blank" rel="noopener">OAuth2</a>等场景。token在幂等方面的贡献只是它的唯一性，我们进行选择的时候主要的是根据业务场景确定为唯一值，也可以是随机生成的只要保证它的唯一性，保证在做幂等号的时候不会出现问题。</li>
</ol>
<h4 id="5-两种具体处理情况"><a href="#5-两种具体处理情况" class="headerlink" title="5. 两种具体处理情况"></a>5. 两种具体处理情况</h4><ol>
<li>拒绝策略（重复的请求过来进行忽略处理，主要的场景是调用方不需要返回结果）</li>
<li>相同响应策略（重复的请求过来进行上一次结果的返回）</li>
</ol>
<h4 id="6-执行方案"><a href="#6-执行方案" class="headerlink" title="6. 执行方案"></a>6. 执行方案</h4><table>
<thead>
<tr>
<th>场景示例</th>
<th>策略</th>
<th>解决方案</th>
</tr>
</thead>
<tbody><tr>
<td>其它系统调用</td>
<td>相同响应策略</td>
<td>解决方案一,解决方案二</td>
</tr>
<tr>
<td>批处理任务调度异常，重复调用</td>
<td>拒绝策略</td>
<td>解决方案一,解决方案二</td>
</tr>
<tr>
<td>批处理任务同时唤醒多台机器执行同一个任务</td>
<td>拒绝策略</td>
<td>解决方案三</td>
</tr>
<tr>
<td>批处理断点续起问题</td>
<td>相同响应策略</td>
<td>解决方案一,解决方案二</td>
</tr>
</tbody></table>
<h5 id="方案一"><a href="#方案一" class="headerlink" title="方案一"></a>方案一</h5><p>依赖数据库方式<br><img src="evernotecid://77FB468C-3E88-43B1-A06D-88D14FB04A37/appyinxiangcom/8577930/ENResource/p208" alt="5800a93fa76068620d664c8b9ef2cb97.png"></p>
<h5 id="方案二"><a href="#方案二" class="headerlink" title="方案二"></a>方案二</h5><p>依赖redis, 其它系统调用的情况下，系统异常重试：（调用系统提供唯一请求ID)<br><img src="evernotecid://77FB468C-3E88-43B1-A06D-88D14FB04A37/appyinxiangcom/8577930/ENResource/p209" alt="9738b91e71d108a156abd62e8b8afb7b.png"></p>
<h5 id="方案三"><a href="#方案三" class="headerlink" title="方案三"></a>方案三</h5><p>主要是依赖于数据库状态字段来对数据是否处理进行标识提高可靠性<br><img src="evernotecid://77FB468C-3E88-43B1-A06D-88D14FB04A37/appyinxiangcom/8577930/ENResource/p207" alt="4bb95b6ed82d2c7ca8e1ba8fbaa29363.png"></p>
<ul>
<li>不断的加锁是会对性能造成影响的。而且增加程序的复杂性，所以我们在操作的时候是需要对程序进行充分的评估来确定加锁时间，加多少锁和使用什么样的锁</li>
<li>执行批处理的任务是可以进行优化，主要是针对获取数据锁，每个数据获取表里面都是有主键或者是包含业务性的字段，我们可以通过最简单的hash分块将其分成多个粒度，类似于1.7版本的ConcurrentHashMap的分段锁的实现方式</li>
</ul>
<p>PS: 上面所有的锁都是需要有一个超时时间设置，防止程序异常导致所有业务卡死在一个节点里面。</p>
<h4 id="7-决策树"><a href="#7-决策树" class="headerlink" title="7. 决策树"></a>7. 决策树</h4><p><a href="http://naotu.baidu.com/file/6850eb6c113d71bd7726e53029c67e73?token=2fa297e97609986f" target="_blank" rel="noopener">决策树引导</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/%E5%8F%AF%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C/" data-id="ck32z8ytt000669dbeumm7ggu"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-Effective Java 阅读笔记（之创建和销毁对象）" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/Effective%20Java%20%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B9%8B%E5%88%9B%E5%BB%BA%E5%92%8C%E9%94%80%E6%AF%81%E5%AF%B9%E8%B1%A1%EF%BC%89/">Effective Java 阅读笔记（之创建和销毁对象）</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/Effective%20Java%20%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B9%8B%E5%88%9B%E5%BB%BA%E5%92%8C%E9%94%80%E6%AF%81%E5%AF%B9%E8%B1%A1%EF%BC%89/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a><strong>引言</strong></h2><p>本博客的内容完全是为记录自己的一些读书感悟，如果喜欢可以进行阅读如有问题请指正，谢谢。</p>
<h2 id="思考"><a href="#思考" class="headerlink" title="思考"></a><strong>思考</strong></h2><ol>
<li>什么是构造器，什么是静态工厂方法？</li>
<li>什么时候用构造器，什么时候用静态工厂方法？</li>
</ol>
<h2 id="静态工厂方法和构造器"><a href="#静态工厂方法和构造器" class="headerlink" title="静态工厂方法和构造器"></a><strong>静态工厂方法和构造器</strong></h2><p><strong>静态工厂方法的优点：</strong></p>
<ol>
<li>静态方法是有名称的，有具体的名称更易于使用者去阅读和了解其功能。如果我需要多个构造器时，使用静态工厂方法可能是最好的。多个构造器除了参数类型等区分不同的功能再没有其他的方式，极易用错。但是静态的构工厂方法可以为每一个提供不同的名称，很容易区分。这种情况使用静态工厂是个好的实现方式。</li>
<li>静态工厂方法了解加载机制的都知道，它不会再每一次调用时都构建一个新的对象，对于频繁调用时是一个不错的方式。</li>
<li>静态工厂方法有返回值，在某种用途上我们能够更好的实现。例如后面的构建器。</li>
<li>实例化参数类型时更加的简洁明了。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实例化时</span></span><br><span class="line">Map&lt;String, List&lt;String&gt;&gt; map = <span class="keyword">new</span> HashMap&lt;String, List&lt;String&gt;&gt;();</span><br><span class="line"><span class="comment">//Google Guava提供的静态实例化方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span>&lt;K, V&gt; HashMap&lt;K, V&gt; <span class="title">newHashMap</span><span class="params">()</span></span>&#123;</span><br><span class="line">	reutrn <span class="keyword">new</span> HashMap&lt;K, V&gt;();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//实例化时</span></span><br><span class="line">Map&lt;String, List&lt;String&gt;&gt; map = Maps.newHashMap();</span><br></pre></td></tr></table></figure>

<p><strong>静态工厂方法的缺点：</strong></p>
<ol>
<li>类如果没有构造器，就不能被子类去类化。</li>
<li>它们与其他的静态方法没什么区别。API文档中不会提供静态方法的说明，有时候你可能只看文档无法知道如何实例化一个类（当然看源码很容易就知道了）</li>
</ol>
<h2 id="使用构建器"><a href="#使用构建器" class="headerlink" title="使用构建器"></a><strong>使用构建器</strong></h2><ol>
<li>使用构造器是解决静态工厂和构造器的局限性，它们不能很好的扩展到大量的可选择参数。</li>
<li>普通的要灵活的使用构造的参数个数可能要写大量的构造器去处理，相当的繁琐。</li>
<li>前期可能使用的参数较少感觉使用构建器反而更复杂，但是后期扩展时很方便。</li>
</ol>
<p>构建器的实现：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">	public class Build &#123;</span><br><span class="line"></span><br><span class="line">    private String name;</span><br><span class="line">    private Integer age;</span><br><span class="line">    private String desc;</span><br><span class="line"></span><br><span class="line">    public static class BuildForm &#123;</span><br><span class="line">        private String name;</span><br><span class="line">        private Integer age;</span><br><span class="line">        private String desc;</span><br><span class="line"></span><br><span class="line">        public BuildForm() &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public BuildForm nameBuild(String name) &#123;</span><br><span class="line">            this.name = name;</span><br><span class="line">            return this;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public BuildForm ageBuild(Integer age) &#123;</span><br><span class="line">            this.age = age;</span><br><span class="line">            return this;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public BuildForm descBuild(String decs) &#123;</span><br><span class="line">            this.desc = decs;</span><br><span class="line">            return this;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public Build buildData() &#123;</span><br><span class="line">            return new Build(this);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    //创建私有构造器防止调用默认构造器处理和处理内部赋值</span><br><span class="line">    private Build(BuildForm buildForm)&#123;</span><br><span class="line">        this.name = buildForm.name;</span><br><span class="line">        this.age = buildForm.age;</span><br><span class="line">        this.desc = buildForm.desc;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public String toString() &#123;</span><br><span class="line">        return &quot;Build&#123;&quot; +</span><br><span class="line">                &quot;name=&apos;&quot; + name + &apos;\&apos;&apos; +</span><br><span class="line">                &quot;, age=&quot; + age +</span><br><span class="line">                &quot;, desc=&apos;&quot; + desc + &apos;\&apos;&apos; +</span><br><span class="line">                &apos;&#125;&apos;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>构建器的使用：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">public class Main &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] argv) &#123;</span><br><span class="line">        Build buildOne = new Build.BuildForm()</span><br><span class="line">        .nameBuild(&quot;AA&quot;)</span><br><span class="line">        .buildData();</span><br><span class="line">        Build buildTwo = new Build.BuildForm()</span><br><span class="line">        .nameBuild(&quot;BB&quot;)</span><br><span class="line">        .ageBuild(12)</span><br><span class="line">        .buildData();</span><br><span class="line">        Build buildThree = new Build.BuildForm()</span><br><span class="line">        .nameBuild(&quot;CC&quot;)</span><br><span class="line">        .ageBuild(12)</span><br><span class="line">        .descBuild(&quot;啊哈哈&quot;)</span><br><span class="line">        .buildData();</span><br><span class="line"></span><br><span class="line">        System.out.println(buildOne.toString());</span><br><span class="line">        System.out.println(buildTwo.toString());</span><br><span class="line">        System.out.println(buildThree.toString());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试结果如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Build&#123;name=&apos;AA&apos;, age=null, desc=&apos;null&apos;&#125;</span><br><span class="line">Build&#123;name=&apos;BB&apos;, age=12, desc=&apos;null&apos;&#125;</span><br><span class="line">Build&#123;name=&apos;CC&apos;, age=12, desc=&apos;啊哈哈&apos;&#125;</span><br></pre></td></tr></table></figure>

<p>以上就是一个简单的构建器，大家可以根据自己的需求去使用。</p>
<h2 id="单例属性的强化"><a href="#单例属性的强化" class="headerlink" title="单例属性的强化"></a><strong>单例属性的强化</strong></h2><p><strong>私有的构造器</strong></p>
<p>就如同我上面代码中写到的，如果不使用私有的构造器去处理类，虽然我们的实现是单例的，但是使用者完全可以使用默认的构造器去创建出来新的对象，有时候就违背我们的初衷。</p>
<p><strong>枚举的方式</strong></p>
<p>我们可以使用枚举的方式去创建一个单例模式，这样就不会出现上述的情况。创建的方式如下代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">//创建一个类</span><br><span class="line">public class Factory &#123;</span><br><span class="line">&#125;</span><br><span class="line">//枚举的方式实例化类</span><br><span class="line">public enum  EnumFactory &#123;</span><br><span class="line">    DATE;</span><br><span class="line">    private Factory factory;</span><br><span class="line">    public void buildSingleton()&#123;</span><br><span class="line">        factory = new Factory();</span><br><span class="line">    &#125;</span><br><span class="line">    public Factory getFactory()&#123;</span><br><span class="line">        return factory;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">//类的使用</span><br><span class="line">public class Main &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] argv) &#123;</span><br><span class="line">        Factory factory = EnumFactory.DATE.getFactory();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>三种单例的创建模式，枚举的方式可能是现在做好的方法。</p>
<h2 id="对象的销毁"><a href="#对象的销毁" class="headerlink" title="对象的销毁"></a><strong>对象的销毁</strong></h2><p><strong>内存泄露</strong></p>
<p>对象有创建必然存在销毁，java提供有自己的垃圾回收机制。但是并不代表java不会出现内存泄露。所以我们需要消除过期的对象引用，若过期的对象引用一直存在垃圾回收器就不会去回收资源。一般这种情况发生在引用缓存中。开发中要注意这类情况。下面是三种容易发生内存泄露的情况。</p>
<ol>
<li>无意识的泄露，一般是代码考虑不周全导致，没有考虑泄露的意识。</li>
<li>缓存中存放的对象，一般需要去维护，不然容易出现泄露。</li>
<li>监听器和回调，这种情况一般是需要维护对象的长期有效的，但是使用完成如果不进行注销就会出现泄露。</li>
</ol>
<p><strong>避免使用终结方法</strong></p>
<p>java有提供终结方法来让开发者去终结，但是终结方法的线程优先级极低，我们并不能去确定终结方法一定会执行，有不能确定终结方法执行的时间。当然有些情况是需要去使用终结方法的，例如流的关闭，一般要配合try….finally来使用。</p>
<h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a><strong>结束语</strong></h2><p>一些简单的介绍，如果要很好的了解建议大家亲自去看看这本书。希望浅薄的理解能对你的开发有帮助。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/Effective%20Java%20%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B9%8B%E5%88%9B%E5%BB%BA%E5%92%8C%E9%94%80%E6%AF%81%E5%AF%B9%E8%B1%A1%EF%BC%89/" data-id="ck32z8yte000069db5ycr39vj"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-跨域问题" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98/">跨域问题</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h3 id="跨域问题"><a href="#跨域问题" class="headerlink" title="跨域问题"></a>跨域问题</h3><h4 id="什么是跨域"><a href="#什么是跨域" class="headerlink" title="什么是跨域"></a>什么是跨域</h4><p>javaScript出于安全考虑，对不在同一页面的对象不允许操作</p>
<h4 id="跨域的场景"><a href="#跨域的场景" class="headerlink" title="跨域的场景"></a>跨域的场景</h4><ul>
<li>不同的二级域名</li>
<li>不同的协议</li>
<li>不同的端口号<h4 id="解决跨域问题"><a href="#解决跨域问题" class="headerlink" title="解决跨域问题"></a>解决跨域问题</h4></li>
<li>JSONP（废弃）<br>jsonp通过带有回调函数callback的json来解决跨域问题。但是他支持的仅仅是get请求，其他的请求类型是不支持</li>
<li>CORS<br>CORS（跨域资源共享），屏蔽开发着，通过浏览器自动添加头信息处理，用户无感知。服务端相应的也需要添加相关的头信息。它是会先请求OPTIONS的一个预请求，获取得到跨域支持的http方法，然后确认真正的请求。缺点是部分浏览器并不能支持该方式</li>
<li>中间层搭建<br>java或者node.js来搭建中间层，进行请求转发。缺点是中间多了一层转发对系统消耗和网络开销都是有很大的开销</li>
<li>Nginx反向代理<br>需要搭建一个nginx反向代理服务器，在url请求时进行url转发具体服务器</li>
</ul>
<h4 id="CORS跨域解决"><a href="#CORS跨域解决" class="headerlink" title="CORS跨域解决"></a>CORS跨域解决</h4><h5 id="CORS涉及的请求响应头"><a href="#CORS涉及的请求响应头" class="headerlink" title="CORS涉及的请求响应头"></a>CORS涉及的请求响应头</h5><ul>
<li>Access-Control-Allow-Origin允许客户端访问，支持*或者完整域名两种方式，并且不支持同时配置多个域名</li>
<li>Access-Control-Allow-Methods允许访问的请求方式</li>
<li>Access-Control-Allow-Credentials是否允许请求带有验证信息，若获取客户端下的cookie时需要设置为true</li>
<li>Access-Control-Max-Age缓存配置</li>
<li>Access-Control-Allow-Headers允许服务器访问的客户端请求头用逗号分隔。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public class CORSFilter implements Filter &#123;  </span><br><span class="line">    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException &#123;  </span><br><span class="line">        HttpServletResponse response = (HttpServletResponse) res;  </span><br><span class="line">        response.setHeader(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);  </span><br><span class="line">        response.setHeader(&quot;Access-Control-Allow-Methods&quot;, &quot;GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, PATCH&quot;);  </span><br><span class="line">        response.setHeader(&quot;Access-Control-Max-Age&quot;, &quot;3600&quot;);  </span><br><span class="line">        response.setHeader(&quot;Access-Control-Allow-Headers&quot;, &quot;Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization, Cache-control&quot;);  </span><br><span class="line">        chain.doFilter(req, res);  </span><br><span class="line">    &#125;  </span><br><span class="line">    public void init(FilterConfig filterConfig) &#123;&#125;  </span><br><span class="line">    public void destroy() &#123;&#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line">public class ComponentRestAutoConfiguration &#123;</span><br><span class="line">    /**</span><br><span class="line">     * rest 跨域配置</span><br><span class="line">     */</span><br><span class="line">    @Bean(initMethod=&quot;init&quot;)</span><br><span class="line">    @ConfigurationProperties(prefix = ALLOWEDORIGIN_KEY_PREFIX)</span><br><span class="line">    public AutoConfigInsideCorsFilter insideCorsFilter() &#123;</span><br><span class="line">        return new AutoConfigInsideCorsFilter();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    /**</span><br><span class="line">     * 默认allowedOrigins的属性的key的前缀</span><br><span class="line">     */</span><br><span class="line">    private static final String ALLOWEDORIGIN_KEY_PREFIX = &quot;cors&quot;;</span><br><span class="line">    </span><br><span class="line">    @Provider</span><br><span class="line">    class AutoConfigInsideCorsFilter extends InsideCorsFilter&#123;</span><br><span class="line">        private Map&lt;String, String&gt; origin;</span><br><span class="line">        public Map&lt;String, String&gt; getOrigin() &#123;</span><br><span class="line">            return origin;</span><br><span class="line">        &#125;</span><br><span class="line">        public void setOrigin(Map&lt;String, String&gt; origin) &#123;</span><br><span class="line">            this.origin = origin;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public void init() &#123;</span><br><span class="line">            if(MapUtils.isEmpty(origin)) &#123;</span><br><span class="line">                return ;</span><br><span class="line">            &#125;</span><br><span class="line">            this.allowedOrigins = new HashSet&lt;&gt;(origin.values());</span><br><span class="line">            this.origin = null;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98/" data-id="ck32z8ytv000869dbbu3cdrw8"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
        

<article id="post-JAVA类加载机制" class="article article-type-post" itemscope itemprop="blogPost" data-scroll-reveal>
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h2 itemprop="name">
      <a class="article-title" href="/bolg/2019/10/21/JAVA%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/">JAVA类加载机制</a>
    </h2>
  
  




      </header>
    

    
      <div class="article-meta">
        <a href="/bolg/2019/10/21/JAVA%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/" class="article-date">
  <time datetime="2019-10-21T14:06:44.887Z" itemprop="datePublished">2019-10-21</time>
</a>
        
      </div>
    

    

    <div class="article-entry" itemprop="articleBody">
      


      

      
        <h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a><strong>概述</strong></h2><p>虚拟机把描述类的数据从Class文件加载到内存中，并对数据进行验证，准备，解析，初始化的一个过程，最终是可以被虚拟机直接使用的java类型，这就是类加载的一个简单的过程。<br>Java中的类加载是在运行时加载，这样会比较的消耗性能，但是正是在运行时加载使得java拥有很好的灵活性和可扩展性。</p>
<h2 id="类加载的时机"><a href="#类加载的时机" class="headerlink" title="类加载的时机"></a><strong>类加载的时机</strong></h2><p>类从被加载到内存中开始，到卸载出内存为止。它的生命周期总共七个阶段：加载—-&gt;验证—-&gt;准备—-&gt;解析—-&gt;初始化—-&gt;使用—-&gt;卸载。其中解析这个过程是不确定的，它可能会在初始化后之后，这是为了使java支持运行时的绑定。</p>
<ul>
<li>new ，getstatic，putstatic，invokestatic这四条指令时会触发初始化的操作。<ul>
<li>new是new一个新的对象时会触发初始化。</li>
<li>getstatic是获取静态字段时会触发。</li>
<li>putstatic是设置静态字段时会触发。</li>
<li>invokestatic是调用另一个类的静态方法的时候。<br>PS：需要注意的是getstatic和putstatic被final修饰的，在编译期就放入到常量池中是不会触发的。</li>
</ul>
</li>
<li>使用java.lang.reflect的包方法对类进行反射调用时，如果类没有初始化就需要进行初始的操作。</li>
<li>子类进行初始化时需要对父类先进行初始。</li>
<li>java启动时需要的启动主类，程序的入口。该类就需要进行初始化。</li>
<li>使用JDK1.7的动态语言支持时，如果一个java.lang.invoke.Methondhandle实例最后解析结果REF_getStatic，REF_putStatic，REF_invokeStatic的方法句柄，如果没有进行初始化时会触发初始化。<br>PS：接口的初始化和类初始化不同，接口初始化只和类初始化的子类初始化是需要父类先进行初始化，而且并不是接口父类中的所有都是会初始化。</li>
</ul>
<h2 id="加载"><a href="#加载" class="headerlink" title="加载"></a><strong>加载</strong></h2><p>加载是类加载中前面提到的其中的一个过程。类加载的基本过程：</p>
<ul>
<li>通过全限定类名加载二进制流。</li>
<li>将二进制流代表的静态存储结构转换方法区中运行时的数据结构。</li>
<li>在内存中生成java.lang.Class对象，将这个作为该方法区这个类中各种数据的一个入口。</li>
</ul>
<p>加载分为数组类加载过程和非数组类的加载过程。java的数组类的加载过程其实是有虚拟机直接加载的但是数组中的类型需要类加载机制加载：</p>
<ul>
<li>非数组类加载机制：可控性强既可以有系统类加载器进行加载又可以由用户自定义的类加载器进行加载。（重写一个类加载器的loadClass()方法）。</li>
<li>数组类型的加载机制：数组类型的加载机制如果是引用类型，就使用递归进行加载，并且会在加载的类型上加入一个标志。如果是非引用类型则会把标志与引导类加载器关联。</li>
</ul>
<p>ps：数组类的可见性与它组件的可见性是相同的，如果组件类型不是引用类型的可见性一般设置为public。<br>类加载完成会有一个连接，可能在没完成加载就开始连接，虽然如此但是该顺序是一定的。</p>
<h2 id="验证"><a href="#验证" class="headerlink" title="验证"></a><strong>验证</strong></h2><p>验证的主要目的是保证加载进来的Class文件的字节流包含的信息符合虚拟机的当前的要求，不会有危害自身的数据存在。<br>Java是相对C++语言是安全的语言，例如它有C++不具有的数组越界的检查。这本身就是对自身安全的一一种保护。验证阶段是Java非常重要的一个阶段，它会直接的保证应用是否会被恶意入侵的一道重要的防线，越是严谨的验证机制越安全。验证的四个阶段文件格式验证–&gt;元数据验证–&gt;字节码验证–&gt;符号引用验证。</p>
<ul>
<li>文件格式验证：主要验证字节流是否符合Class文件格式规范，并且能被当前的虚拟机加载处理。<ul>
<li>是否以魔数开头。</li>
<li>主，次版本号是否在当前虚拟机处理的范围之内。</li>
<li>常量池中是否有不被支持的常量类型。</li>
<li>指向常量的中的索引值是否存在不存在的常量或不符合类型的常量。</li>
<li>CONSTANT_Utf8_info型的常量中有不符合utf8格式的编码数据。<br>还有大其它的验证这里就不一一的列举。</li>
</ul>
</li>
<li>元数据验证：对字节码描述的信息进行语义的分析，分析是否符合java的语言语法的规范。</li>
<li>字节码验证：最重要的验证环节，分析数据流和控制，确定语义是合法的，符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。</li>
<li>符号引用验证：主要是针对符号引用转换为直接引用的时候，是会延伸到第三解析阶段，主要去确定访问类型等涉及到引用的情况，主要是要保证引用一定会被访问到，不会出现类等无法访问的问题。</li>
</ul>
<p>虽然验证很重要但是并不是必须的阶段。当然大量重复的验证会相当的花费性能和时间的。<br><strong>准备</strong></p>
<hr>
<p>准备阶段主要是类变量进行分配内存和数据的初始化阶段，所谓的初始化并不是你编码时所定义的变量值。例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public static int age = 20;</span><br></pre></td></tr></table></figure>
<p>数据的初始化并不会将它初始化为20，而是初始化为0，系统有一套自己的初始化值。如下图：</p>
<table>
<thead>
<tr>
<th>数据类型</th>
<th>零值</th>
</tr>
</thead>
<tbody><tr>
<td>int</td>
<td>0</td>
</tr>
<tr>
<td>long</td>
<td>0L</td>
</tr>
<tr>
<td>short</td>
<td>0</td>
</tr>
<tr>
<td>char</td>
<td>‘\u0000’</td>
</tr>
<tr>
<td>byte</td>
<td>0</td>
</tr>
<tr>
<td>boolean</td>
<td>false</td>
</tr>
<tr>
<td>float</td>
<td>0.0f</td>
</tr>
<tr>
<td>double</td>
<td>0.0d</td>
</tr>
<tr>
<td>reference</td>
<td>null</td>
</tr>
<tr>
<td>当然会有特殊的情况，如下面的代码：</td>
<td></td>
</tr>
<tr>
<td><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public static final int value = 20;</span><br></pre></td></tr></table></figure></td>
<td></td>
</tr>
<tr>
<td>这种情况是类的字段时存在ConstantValue属性所指定的字段。用final修饰后出现该属性，加初始化时会直接的使用ConstantValue的属性值，所以会初始化为20。</td>
<td></td>
</tr>
<tr>
<td><strong>解析</strong></td>
<td></td>
</tr>
<tr>
<td>—–</td>
<td></td>
</tr>
</tbody></table>
<p>解析是将常量池中的符号引用转化为直接引用的过程，还记得前面验证阶段时出现的符号引用验证吗？就是对该阶段的验证。</p>
<ul>
<li>符号引用：符号引用是以一组符号来描述所引用的目标，符号可以是任何的字面形式的字面量，只要不会出现冲突能够定位到就行。布局和内存无关。</li>
<li>直接引用：是指向目标的指针，偏移量或者能够直接定位的句柄。该引用是和内存中的而布局有关的，并且一定加载进来的。</li>
</ul>
<p>虚拟机可能会多次的进行解析。解析主要的对类，接口，字段，类方法，接口方法，方法类型，方法句柄和调用点限定符引用进行。这七种解析有细节上的不同，主要的思想是通过限定性类名找到解析的类型进行解析。主要的是会分为数组类型，非数组类型存在一个直接进行解析的过程。在过程还有从下上的匹配查找（主要出现在有继承，接口的情况下）。</p>
<h2 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a><strong>初始化</strong></h2><p>初始化算是类加载过程的最后一个阶段，在这个阶段在是真正的开始有java代码主导。大家应该记得在准备阶段已经进行过一次赋值，但是只是系统的默认赋值（ConstantValue的例外情况）。初始化是执行<clinit>的过程。</p>
<ul>
<li><clinit>的主要是查找static模块，用户自定义类变量的赋值，该顺序是由文件中的顺序界定的。加载过程存在的是父类的一定会比子类先进行加载到，因为会保证子类的<clinit>加载完成时父类的<clinit>一定会加载完成。所有就像大家所知道的java.lang.object一定会是虚拟机中第一个加载完成的。</li>
<li><clinit>在接口中的加载是不同的它是不存在静态块的，接口中也是会有赋值进行的，但是接口中的是在需要用到才会去进行加载的。</li>
<li>允许在定义之前进行赋值的操作，但是不允许使用，如下：<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public class A&#123;</span><br><span class="line">  static&#123;</span><br><span class="line">          s = 20;</span><br><span class="line">          //system.out.printf(s); </span><br><span class="line">          上面注释的这句话时会出现错误的；</span><br><span class="line">  &#125;</span><br><span class="line">  static int s = 10;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>虚拟机会保证在多线程的环境下进行加锁，保证正确执行。如果有多个进行加载一个会保证只有一个去加载，其他的会进去阻塞等待中。同一个类只会加载一次，就算多个进入阻塞也不会重新唤醒。</li>
</ul>
<h2 id="类加载器"><a href="#类加载器" class="headerlink" title="类加载器"></a><strong>类加载器</strong></h2><ul>
<li><p>类与类加载器：一个类的相同判断条件大家都知道，但是如果不是由同一个类加载器加载出来的，就算是看起来相同的也是出现false的。</p>
</li>
<li><p>三大类加载器：</p>
<ul>
<li>启动类加载器</li>
<li>扩展类加载器</li>
<li>应用程序类加载器</li>
</ul>
</li>
<li><p>双亲委托机制：<br><img src="http://upload-images.jianshu.io/upload_images/5862120-dd9c0131d5870696.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="双器委托机制"><br>双亲委托机制是当一个类进入加载时，子加载器不会自己尝试去加载，而是将其发送到它的父加载器中加载，以此类推直到达到最后的加载器，只有当父加载器不能进行加载是会发送到子加载器中，子加载此时才会尝试去加载。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">public Class&lt;?&gt; loadClass(String name)throws ClassNotFoundException &#123;</span><br><span class="line">            return loadClass(name, false);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    protected synchronized Class&lt;?&gt; loadClass(String name, boolean resolve)throws ClassNotFoundException &#123;</span><br><span class="line">            // 首先判断该类型是否已经被加载</span><br><span class="line">            Class c = findLoadedClass(name);</span><br><span class="line">            if (c == null) &#123;</span><br><span class="line">                try &#123;</span><br><span class="line">                    if (parent != null) &#123;</span><br><span class="line">                        c = parent.loadClass(name, false);</span><br><span class="line">                    &#125; else &#123;</span><br><span class="line">                    //如果不存在父类加载器，就检查是否是由启动类加载器加载的类，通过调用本地方法native Class findBootstrapClass(String name)</span><br><span class="line">                        c = findBootstrapClass0(name);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; catch (ClassNotFoundException e) &#123;</span><br><span class="line">                 // 如果父类加载器和启动类加载器都不能完成加载任务，才调用自身的加载功能</span><br><span class="line">                    c = findClass(name);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            if (resolve) &#123;</span><br><span class="line">                resolveClass(c);</span><br><span class="line">            &#125;</span><br><span class="line">            return c;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>双亲委托机制的破坏</p>
<ul>
<li>1.2版本为了向前兼容1.0版本</li>
<li>本身模型的问题，基础类要调用用户类而出现的冲突。通过设置线程上下文类加载器，如果出现上面这种情况，通过上下文类加载器去加载所需的类。</li>
<li>用户对动态性的追求，出现没一个模块都有自己的类加载器，如果需要更换时连同类加载器一同换掉。</li>
</ul>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://m_aleen.gitee.io/bolg/2019/10/21/JAVA%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/" data-id="ck32z8yu5000969dbbmyy2f04"
         class="article-share-link">Share</a>
      
    </footer>

  </div>

  

  

</article>



      
  </article>
  

  
    <nav class="page-nav">
      
      <span class="page-number current">1</span><a class="page-number" href="/bolg/page/2/">2</a><a class="extend next" rel="next" href="/bolg/page/2/">Next page</a>
    </nav>
  
</section>
</div>

  <footer class="footer">
  <div class="outer">
    <div class="float-right">
      <ul class="list-inline">
  
    <li><i class="fe fe-smile-alt"></i> <span id="busuanzi_value_site_uv"></span></li>
  
    <li><i class="fe fe-bookmark"></i> <span id="busuanzi_value_page_pv"></span></li>
  
</ul>
    </div>
    <ul class="list-inline">
      <li>&copy; 2019 M_W_Q</li>
      <li>Powered by <a href="http://hexo.io/" target="_blank">Hexo</a></li>
      <li>Theme  <a href="https://github.com/zhwangart/hexo-theme-ocean" target="_blank" rel="noopener">Ocean</a></li>
    </ul>
  </div>
</footer>

</main>

  <aside class="sidebar">
    
    <button class="navbar-toggle"></button>
<nav class="navbar">
  
    <div class="logo">
      <a href="/bolg/"><img src="/bolg/images/hexo.svg" alt="M_W_Q"></a>
    </div>
  
  <ul class="nav nav-main">
    
      <li class="nav-item">
        <a class="nav-item-link" href="/bolg/">Home</a>
      </li>
    
      <li class="nav-item">
        <a class="nav-item-link" href="/bolg/archives">Archives</a>
      </li>
    
      <li class="nav-item">
        <a class="nav-item-link" href="/bolg/gallery">Gallery</a>
      </li>
    
      <li class="nav-item">
        <a class="nav-item-link" href="/bolg/about">About</a>
      </li>
    
    <li class="nav-item">
      <a class="nav-item-link nav-item-search" title="搜索">
        <i class="fe fe-search"></i>
        Search
      </a>
    </li>
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      <div class="totop" id="totop">
  <i class="fe fe-rocket"></i>
</div>
    </li>
    <li class="nav-item">
      
        <a class="nav-item-link" target="_blank" href="/bolg/atom.xml" title="RSS Feed">
          <i class="fe fe-feed"></i>
        </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
  </aside>
  <script src="/bolg/js/jquery-2.0.3.min.js"></script>
<script src="/bolg/js/jquery.justifiedGallery.min.js"></script>
<script src="/bolg/js/lazyload.min.js"></script>
<script src="/bolg/js/busuanzi-2.3.pure.min.js"></script>

  <script src="/bolg/fancybox/jquery.fancybox.min.js"></script>




<script src="/bolg/js/ocean.js"></script>

</body>
</html>